﻿using System;
using System.Linq;
namespace Bango.WindowsPhone7.SDK
{
	public static class BangoStorage
	{
		private static System.IO.IsolatedStorage.IsolatedStorageSettings _isolatedStore;
		private static System.String _keyName = "BangoStore";

		private static System.Runtime.Serialization.Json.DataContractJsonSerializer _profileSerializer;
		private static System.Runtime.Serialization.Json.DataContractJsonSerializer _eventSerializer;
		private static System.IO.IsolatedStorage.IsolatedStorageFile _isolatedStoreFile;
		//private static System.String _fileName = "BangoStore.db";

		//12-10-11 JP Add new variables to contain Bango Profile Store and Bango Events Store
		private static System.String _profileFileName = "BangoProfileStore.db";
		private static System.String _eventFileName = "e_Default";
		private static System.String _eventFileNameKey = "BangoEventFileName";
		private static System.String _eventFilePrefix = "e_";
		private static System.String _eventDirectoryName = "bangoEvents";
		private static System.String _eventsPerFileKeyName = "EventsPerFile";
		private static System.Int32 _eventsPerFile = 100;

		//12-10-11 JP Alter public property to return _bangoProfileStore instead of _bangoStore
		public static System.Collections.Generic.List<BangoProfile> BangoStore { get { return _bangoProfileStore; } }

		//12-10-11 JP Add new lists for Bango Profiles and Bango Events
		private static System.Collections.Generic.List<BangoProfile> _bangoProfileStore;		
		private static System.Collections.Generic.List<BangoEvent> _bangoEventStore;
		public static System.Collections.Generic.List<BangoEvent> BangoEventStore { get { return _bangoEventStore; } set { _bangoEventStore = value; } }

		internal static System.String EventFileName { get { return _eventFileName; } set { _eventFileName = value; } }
		internal static System.Int32 ProfileId { get; set; }

		//14-10-11 JP Allow events per file to be set by property
		internal static System.Int32 EventsPerFile
		{
			get { return _eventsPerFile; }
			set
			{
				_eventsPerFile = value;
				SaveSetting<System.Int32>(_eventsPerFileKeyName, _eventsPerFile);
			}
		}

		#region JSON Serializer

		internal static void InitStorage(System.Boolean useOfflineStorage)
		{
			_isolatedStore = System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings;
			//13-10-11 JP Load the latest event file name, for filling the event store
			_eventFileName = LoadSetting<System.String>(_eventFileNameKey, _eventFileName);
			System.Collections.Generic.List<BangoEvent> tempEventStore = new System.Collections.Generic.List<BangoEvent>();
			_bangoEventStore = new System.Collections.Generic.List<BangoEvent>();

			if (useOfflineStorage)
			{
				_isolatedStoreFile = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
				_profileSerializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(System.Collections.Generic.List<BangoProfile>));
				_eventSerializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(System.Collections.Generic.List<BangoEvent>));
				try
				{
					//12-10-11 JP With new file configuration, we need to read profile file first, then events files
					//using (System.IO.IsolatedStorage.IsolatedStorageFileStream isoStream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(_fileName, System.IO.FileMode.OpenOrCreate, _isolatedStoreFile))
					using (System.IO.IsolatedStorage.IsolatedStorageFileStream isoStream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(_profileFileName, System.IO.FileMode.OpenOrCreate, _isolatedStoreFile))
					{
						if (isoStream.Length > 0)
						{
							isoStream.Position = 0;
							//_bangoStore = (System.Collections.Generic.List<BangoProfile>)_serializer.ReadObject(isoStream);
							_bangoProfileStore = (System.Collections.Generic.List<BangoProfile>)_profileSerializer.ReadObject(isoStream);
						}

						isoStream.Close();
					}
					//12-10-11 JP Now load up the events separately
					_isolatedStoreFile.CreateDirectory(_eventDirectoryName);
					System.String[] fileNames = _isolatedStoreFile.GetFileNames(_eventDirectoryName + @"\" + "*");
					foreach (string fileName in fileNames)
					{
						//12-11-10 JP Load in all the event file contents, and add the events to all the profiles
						using (System.IO.IsolatedStorage.IsolatedStorageFileStream isoStream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(_eventDirectoryName + @"\" + fileName, System.IO.FileMode.OpenOrCreate, _isolatedStoreFile))
						{
							if (isoStream.Length > 0)
							{
								isoStream.Position = 0;
								tempEventStore = (System.Collections.Generic.List<BangoEvent>)_eventSerializer.ReadObject(isoStream);
							}
							isoStream.Close();
							if (tempEventStore.Count > 0)
							{
								foreach (BangoEvent ev in tempEventStore)
								{
									//12-10-11 JP Add each event to its profile
									foreach (BangoProfile myProfile in _bangoProfileStore)
									{
										if (myProfile.ProfileId == ev.ProfileId)
										{
											myProfile.Events.Add(ev);
											break;
										}
									}
									//12-10-11 JP If we are in the latest file, add the event to the current event store
									if (fileName == _eventFileName)
									{
										_bangoEventStore.Add(ev);
									}
								}
							}
						}
					}
					//13-10-11 JP Now for each of the profiles, re-order the added events by event id in case the
					//files weren't loaded in order
					foreach (BangoProfile profileToSort in _bangoProfileStore)
					{
						profileToSort.Events = profileToSort.Events.OrderBy(e => e.EventId).ToList();
					}
				}
				catch (System.Exception ex)
				{
					System.Diagnostics.Debug.WriteLine(ex.Message);
				}

				System.Diagnostics.Debug.WriteLine("TEST");
			}

			//12-10-11 Initialize Profile store and event store
			if (_bangoProfileStore == null)
			{
				_bangoProfileStore = new System.Collections.Generic.List<BangoProfile>();
			}
			if (_bangoEventStore == null)
			{
				_bangoEventStore = new System.Collections.Generic.List<BangoEvent>();
			}
		}

		internal static void SaveProfileStore(System.Boolean useOfflineStorage)
		{
			try
			{
				if (useOfflineStorage && _isolatedStoreFile != null)
				{
					//12-10-11 JP Get copy of profile store with events stripped out
					System.Collections.Generic.List<BangoProfile> tempProfileStore = new System.Collections.Generic.List<BangoProfile>();
					foreach (BangoProfile myProfile in _bangoProfileStore)
					{
						BangoProfile profileToAdd = myProfile.GetProfileForSave();
						tempProfileStore.Add(profileToAdd);
					}

					using (System.IO.MemoryStream jsonStream = new System.IO.MemoryStream())
					{
						_profileSerializer.WriteObject(jsonStream, tempProfileStore);
						jsonStream.Position = 0;
						using (System.IO.IsolatedStorage.IsolatedStorageFileStream isoStream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(_profileFileName, System.IO.FileMode.Create, _isolatedStoreFile))
						{
							if (isoStream != null)
							{

								_profileSerializer.WriteObject(isoStream, tempProfileStore);
							}

							isoStream.Close();
						}
					}
				}
			}
			catch (System.Exception)
			{
			}
		}

		internal static void SaveEventStore(System.Boolean useOfflineStorage)
		{
			try
			{
				if (useOfflineStorage && _isolatedStoreFile != null)
				{
					using (System.IO.MemoryStream jsonStream = new System.IO.MemoryStream())
					{
						_eventSerializer.WriteObject(jsonStream, _bangoEventStore);
						jsonStream.Position = 0;
						_isolatedStoreFile.CreateDirectory(_eventDirectoryName);
						using (System.IO.IsolatedStorage.IsolatedStorageFileStream isoStream = new System.IO.IsolatedStorage.IsolatedStorageFileStream(_eventDirectoryName + @"\" +  _eventFileName, System.IO.FileMode.Create, _isolatedStoreFile))
						{
							if (isoStream != null)
							{

								_eventSerializer.WriteObject(isoStream, _bangoEventStore);
							}

							isoStream.Close();
						}
					}
				}
			}
			catch (System.Exception ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.Message);
			}
		}

		#endregion

		#region Settings

		public static System.Boolean SaveSetting<T>(System.String keyName, T keyValue)
		{
			System.Boolean returnValue = false;
			System.String combinedKeyName = _keyName + "_" + keyName;
			try
			{
				if (_isolatedStore.Contains(combinedKeyName))
				{
					_isolatedStore[combinedKeyName] = keyValue;
				}
				else
				{
					_isolatedStore.Add(combinedKeyName, keyValue);
				}
				_isolatedStore.Save();
				returnValue = true;

			}
			catch (System.Exception)
			{
				returnValue = false;
			}
			return returnValue;
		}

		public static T LoadSetting<T>(System.String keyName, T defaultValue)
		{
			T returnValue = defaultValue;
			System.String combinedKeyName = _keyName + "_" + keyName;
			try
			{
				if (_isolatedStore.Contains(combinedKeyName))
				{
					try
					{
						_isolatedStore.TryGetValue<T>(combinedKeyName, out returnValue);
					}
					catch (System.Exception)
					{
						returnValue = defaultValue;
					}
				}
				else
				{
					returnValue = defaultValue;
				}
			}
			catch (System.Exception)
			{

			}
			return returnValue;
		}

		#endregion

		internal static BangoProfile GetProfile(System.Int32 profileId)
		{
			BangoProfile profile = null;

			if (_bangoProfileStore != null)
			{
				for (System.Int32 profileCounter = 0; profileCounter < _bangoProfileStore.Count; profileCounter++)
				{
					if (profileId.Equals(_bangoProfileStore[profileCounter].ProfileId))
					{
						profile = _bangoProfileStore[profileCounter];
						break;
					}
				}
			}
			return profile;
		}

		internal static void LogProfile(System.Boolean useOfflineStorage, System.Int32 sdkType, System.String sdkVersion, System.String applicationId, System.String applicationType, System.String applicationChannel, System.String applicationVersion, System.String userId, System.Int32 sessionRestartTime, System.Int32 connectionType, System.Boolean isRoaming, System.String locationSource, System.Double locationAltitude, System.Double locationLatitude, System.Double locationLongitude, System.Double locationAccuracy, System.String operatorCode, System.String operatorName, System.String operatorCountryIso, System.String simOperator, System.String brand, System.String device, System.String model, System.String product, System.String version, System.String buildId, System.Int32 operatorConnectionType)
		{
			System.Int32 profileId = -1;

			if (applicationId == null)
			{
				applicationId = "";
			}

			if (applicationType == null)
			{
				applicationType = "";
			}

			if (applicationChannel == null)
			{
				applicationChannel = "";
			}

			if (applicationVersion == null)
			{
				applicationVersion = "";
			}

			if (userId == null)
			{
				userId = "";
			}

			if (locationSource == null)
			{
				locationSource = "";
			}

			if (operatorCode == null)
			{
				operatorCode = "";
			}

			if (operatorName == null)
			{
				operatorName = "";
			}

			if (operatorCountryIso == null)
			{
				operatorCountryIso = "";
			}

			if (simOperator == null)
			{
				simOperator = "";
			}

			if (brand == null)
			{
				brand = "";
			}

			if (device == null)
			{
				device = "";
			}

			if (model == null)
			{
				model = "";
			}

			if (product == null)
			{
				product = "";
			}

			if (version == null)
			{
				version = "";
			}

			if (buildId == null)
			{
				buildId = "";
			}

			if (_bangoProfileStore.Count > 0)
			{
				profileId = _bangoProfileStore.Max(p => p.ProfileId);
			}
			else
			{
				profileId = 0;
			}
			profileId ++;
			//12-10-11 JP Add profile to profile store instead of Bango Store
			_bangoProfileStore.Add(new BangoProfile(profileId, sdkType, sdkVersion, applicationId, applicationType, applicationChannel, applicationVersion, userId, connectionType, sessionRestartTime, isRoaming, locationSource, locationAltitude, locationLatitude, locationLongitude, locationAccuracy, operatorCode, operatorName, operatorCountryIso, brand, device, model, product, version, buildId, operatorConnectionType, simOperator));
			ProfileId = profileId;
			BangoStorage.SaveSetting<System.Int32>("ProfileId", ProfileId);

			//12-10-11 JP Save Profile Store instead of Bango Store
			SaveProfileStore(useOfflineStorage);
		}

		internal static void UpdateProfileLocation(System.Boolean useOfflineStorage, System.Double locationLatitude, System.Double locationLongitude, System.Double locationAltitude, System.Double locationAccuracy)
		{
			BangoProfile profile = null;
			profile = GetProfile(ProfileId);
			if (profile != null)
			{
				profile.LocationX = locationLongitude;
				profile.LocationY = locationLatitude;

				if (double.IsNaN(locationAltitude))
				{
					profile.LocationZ = 0;
				}
				else
				{
					profile.LocationZ = locationAltitude;
				}

				if (double.IsNaN(locationAccuracy))
				{
					profile.LocationAccuracy = 0;
				}
				else
				{
					profile.LocationAccuracy = locationAccuracy;
				}

				SaveProfileStore(useOfflineStorage);
			}
		}

		internal static System.Int32 LogEvent(System.Boolean useOfflineStorage, System.Int32 connectionType, System.Int32 operatorConnectionType, System.Int32 errorLevel, System.String eventName, System.String eventDetail, System.String eventValue, System.String errorId, System.DateTime eventTime, System.DateTime eventLocalTime, System.Collections.Generic.List<BangoEventParameter> eventParameters)
		{
			BangoProfile profile = null;
			System.Int32 eventId = -1;

			if (eventName == null)
			{
				eventName = "";
			}

			if (eventDetail == null)
			{
				eventDetail = "";
			}

			if (eventValue == null)
			{
				eventValue = "";
			}

			if (errorId == null)
			{
				errorId = "";
			}

			profile = GetProfile(ProfileId);
			if (profile != null)
			{
				eventId = profile.Events.Count + 1;
				//12-10-11 JP We need to save events separately. Add the new event to the event store as well as the profile
				//profile.Events.Add(new BangoEvent(ProfileId, eventId, connectionType, operatorConnectionType, errorLevel, eventName, eventDetail, eventValue, errorId, eventTime, eventLocalTime, eventParameters));
				BangoEvent newEvent = new BangoEvent(ProfileId, eventId, connectionType, operatorConnectionType, errorLevel, eventName, eventDetail, eventValue, errorId, eventTime, eventLocalTime, eventParameters);
				profile.Events.Add(newEvent);
				if (_bangoEventStore.Count >= _eventsPerFile)
				{
					_bangoEventStore = new System.Collections.Generic.List<BangoEvent>();
					_bangoEventStore.Add(newEvent);
					_eventFileName = GetNewEventFileName();
				}
				else
				{
					_bangoEventStore.Add(newEvent);
				}

				SaveEventStore(useOfflineStorage);
			}
			return eventId;
		}

		internal static System.Boolean UpdateEventsPosted(System.Boolean useOfflineStorage, System.Int32 profileId, System.Int32 maxEventId)
		{
			System.Boolean returnValue = false;
			BangoProfile profile = null;
			System.Collections.Generic.List<BangoEvent> bangoEvents;
			try
			{

				profile = GetProfile(profileId);
				if (profile != null)
				{
					bangoEvents = (from ev in profile.Events where ev.EventId <= maxEventId && ev.Posted == false select ev).ToList();


					for (System.Int32 eventCounter = 0; eventCounter < bangoEvents.Count; eventCounter++)
					{
						bangoEvents[eventCounter].Posted = true;
					}

					SaveEventStore(useOfflineStorage);
				}
				returnValue = true;
			}
			catch (System.Exception)
			{
			}
			return returnValue;

		}

		//11-10-11 JP New function to delete posted events rather than updating them - maintains high performance in logging/storage after each posting
		internal static System.Boolean DeleteEventsPosted(System.Boolean useOfflineStorage, System.Int32 profileId, System.Int32 maxEventId)
		{
			System.Boolean returnValue = false;
			BangoProfile profile = null;
			System.Collections.Generic.List<BangoEvent> bangoEvents;
			try
			{

				profile = GetProfile(profileId);
				if (profile != null)
				{
					bangoEvents = (from ev in profile.Events where ev.EventId <= maxEventId && ev.Posted == false select ev).ToList();


					for (System.Int32 eventCounter = 0; eventCounter < bangoEvents.Count; eventCounter++)
					{
						profile.Events.Remove(bangoEvents[eventCounter]);
					}

					SaveEventStore(useOfflineStorage);
				}
				returnValue = true;
			}
			catch (System.Exception)
			{
			}
			return returnValue;

		}

		internal static System.Boolean DeleteProfilePosted(System.Boolean useOfflineStorage, BangoProfile profile)
		{
			System.Boolean returnValue = false;

			//12-10-11 JP Change this function to save the profiles to the new profile store
			try
			{
				if (_bangoProfileStore != null)
				{
					_bangoProfileStore.Remove(profile);
					SaveProfileStore(useOfflineStorage);
				}
				returnValue = true;
			}
			catch (System.Exception)
			{

			}

			return returnValue;

		}

		//13-10-11 JP New function added to get new name for event file
		internal static System.String GetNewEventFileName()
		{
			System.String myGuidString = Guid.NewGuid().ToString();
			System.String eventFileName = _eventFilePrefix + myGuidString;
			BangoStorage.SaveSetting<System.String>(_eventFileNameKey, BangoStorage.EventFileName);
			return eventFileName;
		}

		//13-10-11 JP New function added to get the event file names to delete
		internal static System.String[] GetFileNamesToDelete()
		{
			return _isolatedStoreFile.GetFileNames(_eventDirectoryName + @"\" + _eventFilePrefix + "*");
		}

		//13-10-11 JP New function added to delete the old event files from storage
		internal static System.Boolean DeleteOldEventFiles(System.Boolean useOfflineStorage, System.String[] eventFilesToDelete)
		{
			foreach (System.String myFileName in eventFilesToDelete)
			{
				_isolatedStoreFile.DeleteFile(_eventDirectoryName + @"\" + myFileName);
			}
			return true;
		}
	}
}
